/**
 * __系统顶级类：$D__，用于动态页面处理
 * @class $D
 */
define([
	'js/ctls' + $E.jsv,
	'js/ctls2' + $E.jsv,
	'js/instance' + $E.jsv,
	'js/component' + $E.jsv
], function(Ctls, Ctls2, Instance, Component) {
	var $D = {
		/**
		 * 插件版本号
		 * @property version
		 * @type {string}
		 */
		version : '1.0.20190119',

		/**
		 * 动态页面主结构数据缓存
		 *
		 * 	structs : {
		 * 		projectId : {
		 * 			key: {string} 动态页面ID
		 * 			struct: {object} 动态页面主结构数据
		 * 		},
		 * 		...
		 * 	}
		 *
		 * @property structs
		 * @type {object}
		 */
		structs : {},

		Component: Component,

		/**
		 * 获取一个动态页面主结构
		 * @method getStruct
		 * @param project {object} 项目对象
		 * @param dynId {string} 动态页面ID
		 * @return {object} 动态页面结构主数据
		 */
		getStruct : function(project, dynId, type) {
			if (! this.structs[project.id]) {
				this.structs[project.id] = {
					DYN: {},
					DYN2: {}
				};
			}
			var dynType = (type || 'DYN').toUpperCase();
			var struct = this.structs[project.id][dynType][dynId];
			if (! struct) {
				struct = this.loadStruct(project, dynId, dynType);
				this.structs[project.id][dynType][dynId] = struct;
			}
			return struct;
		},

		removeStruct : function(project, dynId) {
			if (this.structs[project.id]) {
				delete this.structs[project.id].DYN2[dynId];
			}
		},

		/**
		 * 从服务器加载动态页面的主结构数据
		 * @method getStruct
		 * @param project {object} 项目对象
		 * @param dynId {string} 动态页面ID
		 * @param dynType {string} DYN or DYN2
		 * @return {object} 动态页面结构主数据
		 */
		loadStruct : function(project, dynId, dynType) {
			var struct, start = new Date();
			if (dynType == 'DYN') {
				$F.logGroup('Dyn - loadStruct', '加载动态页面主结构, ID: ' + dynId);
				$F.dataService('SysCacheJSON', {
					data : {
						Key : project.token,
						DBKey : '',
						Name : 'Dyn.Build',
						O : 'M',
						MainID : dynId,
						SubID : '',
						lang: ($E.lang == 'zh' ? '' : $E.lang)
					},
					digest: project.digest,
					async : false,
					success : function(data) {
						if (data.code > 0) {
							struct = data.value;
						} else
							$F.alert(data.message);
					}
				});
				if (! struct) {
					$F.err('Dyn - loadStruct', '数据接口异常，没有获得结构缓存');
					$F.logGroupEnd()
				} else {
					struct.id = struct.GroupIdA;
					struct.guid = struct.GroupId;
					struct.name = struct.na;
					struct.LocList = struct.LocList || {};
					struct.CtlCmdList = {};
					struct.pages = {};
					struct.version = 'DYN';
					delete struct.na;
					$F.logGroupEnd('Dyn - loadStruct', '加载主结构完成，耗时' + (new Date() - start) + 'ms', struct);
				}
			} else {
				$F.logGroup('Dyn - loadStruct', '加载动态页面2主结构, ID: ' + dynId);
				var struct, start = new Date(), dbpool = project.getParam('CFGDBP');
				if ($E.publishVersion) {
					$F.ngsService({
						url: $E.url + '/dynStruct_',
						data: {
							dynId: dynId,
							dbPool: dbpool
						},
						async: false,
						success: function(data) {
							if (data.code > 0) {
								var bean = data.value,
									content = (bean.CONTENT ? JSON.parse(bean.CONTENT) : {
										cmds:{
											internal:{}
										}
									}),
									property,
									prop;
								struct = {
									id: bean.DYNID2,
									guid: bean.DYNID,
									name: bean.DYNNAME,
									stateful: bean.STATEFUL,
									theme: bean.THEME,
									trackablity: bean.TRACKABLITY,
									cmds: content.cmds,
									flows: content.flows,
									styles: content.styles || {},
									props: {
										pub: {},
										local: {}
									},
									pages: {},
									i18n: content.i18n || {},
									local: content.local || {},
									version: 'DYN2',
									publishVersion: bean.VERSION,
									publishedTime: bean.PUBLISHEDTIME
								};
								for(var key in content.properties) {
									for(var name in content.properties[key]) {
										prop = content.properties[key][name];
										property = {
											ns: key,
											field: name,
											t: prop.t,
											e: prop.e
										};
										if (prop.v !== undefined)
											property.v = prop.v;
										if (key == 'var' || key == 'tabc') {
											struct.props.local[key + '.' + name] = property
										} else {
											struct.props.pub[key + '.' + name] = property
										}
									}
								}

								$F.dataService('SystemDo2', {
									data: $F.sqlbean2({VE:'publish.dyn.i18n.findAll'}, project.token, dbpool, [struct.guid, struct.publishVersion]),
									digest: project.digest,
									async: false,
									success: function(data) {
										if (data.code > 0) {
											$.each(data.value[0].fields, function(i, v) {
												struct.i18n[v[0]] = JSON.parse(v[1])
											})
										} else
											$F.alert(data.message)
									}
								})
							}
						}
					}, project.token, project.digest);
				} else {
					$F.dataService('SystemDo2', {
						data: $F.sqlbean2("dyn2.getById2", project.token, dbpool, [dynId]),
						async: false,
						digest: project.digest,
						success: function(data){
							if (data.code > 0) {
								if (data.value[0].count > 0) {
									var bean = $F.makeJsonBean(data.value[0]),
										content = (bean.CONTENT ? JSON.parse(bean.CONTENT) : {
											cmds:{
												internal:{}
											}
										}),
										property,
										prop;
									struct = {
										id: bean.DYNID2,
										guid: bean.DYNID,
										name: bean.DYNNAME,
										stateful: bean.STATEFUL,
										theme: bean.THEME,
										trackablity: bean.TRACKABLITY,
										cmds: content.cmds,
										flows: content.flows,
										styles: content.styles || {},
										props: {
											pub: {},
											local: {}
										},
										pages: {},
										i18n: content.i18n || {},
										local: content.local || {},
										version: 'DYN2'
									};
									for(var key in content.properties) {
										for(var name in content.properties[key]) {
											prop = content.properties[key][name];
											property = {
												ns: key,
												field: name,
												t: prop.t,
												e: prop.e
											};
											if (prop.v !== undefined)
												property.v = prop.v;
											if (key == 'var' || key == 'tabc') {
												struct.props.local[key + '.' + name] = property
											} else {
												struct.props.pub[key + '.' + name] = property
											}
										}
									}

									$F.dataService('SystemDo2', {
										data: $F.sqlbean2('VE.dyn.i18n.findAll', project.token, dbpool, [struct.guid]),
										digest: project.digest,
										async: false,
										success: function(data) {
											if (data.code > 0) {
												$.each(data.value[0].fields, function(i, v) {
													struct.i18n[v[0]] = JSON.parse(v[1])
												})
											} else
												$F.alert(data.message)
										}
									})
								}
							} else {
								$F.alert(data.message);
							}
						}
					})
				}

				if (! struct) {
					$F.err('Dyn - loadStruct', '数据接口异常，没有获得结构缓存');
					$F.logGroupEnd()
				} else {
					$F.logGroupEnd('Dyn - loadStruct', '加载主结构完成，耗时' + (new Date() - start) + 'ms', struct);
				}
			}
			return struct;
		},

		/**
		 * 动态页面全局命令组
		 *
		 *	pcmds : {
		 *		key: {string} 命令组GUID
		 *		cmds: {array} 命令组
		 *	}
		 *
		 * @property pcmds
		 * @type {map}
		 */
		pcmds : {},

		/**
		 * 创建动态页面实例
		 * @method createInstance
		 * @param project {object} 项目对象
		 * @param dynId {string} 动态页面ID
		 * @param otId {string} 市场活动ID
		 * @param oId {string} objectiveGUID
		 * @param parameters {any} 入参动态页面的局部变量
		 * @param [inner] {boolean} 作为组件模式创建，默认为false
		 * @param [type] {string} 动态页面类型，动态页面='DYN'，工单='WO'，客户资料='CUST'。默认为'DYN'
		 * @param [veSupport] {boolean} 是否VE类型动态页面，默认为false
		 */
		createInstance : function(project, dynId, otId, oId, parameters, inner, type, veSupport) {
			// $F.log('Dyn - createInstance', 'dynId:' + dynId + ', otId:' + otId + ', oId:' + oId + ', type:' + type + ', ve:' + veSupport);
			var struct = this.getStruct(project, dynId, veSupport ? 'DYN2' : 'DYN');
			if (! struct)
				return null;

			var instance = Instance.create(project, struct, otId, oId, parameters, inner, type);
			if (instance.stateful && type !== 'WO' && ! project.getCustomerGuid()) {
				$F.alert('当前无客户对象，不能操作有状态页面');
				return;
			}
			project.events.register($CONST.CTIEvent.CTI_ONCALLSTART, instance.id, function(){
				if (instance.struct.version === 'DYN2') {
					instance.doCmdIncludeComponents('cti.callstart', {}, instance.runtime, instance.id, true);
				} else {
					instance.doCmdIncludeComponents("P-234");
				}
			});
			project.events.register($CONST.CTIEvent.CTI_ONCALLESTABLISH, instance.id, function(){
				if (instance.struct.version === 'DYN2') {
					instance.doCmdIncludeComponents('cti.callestablish', {}, instance.runtime, instance.id, true);
				} else {
					instance.doCmdIncludeComponents("P-235");
				}
			});
			project.events.register($CONST.CTIEvent.CTI_ONCALLEND, instance.id, function(){
				if (instance.struct.version === 'DYN2') {
					instance.doCmdIncludeComponents('cti.callend', {}, instance.runtime, instance.id, true);
				} else {
					instance.doCmdIncludeComponents("P-236");
				}
			});
			project.events.register($CONST.CTIEvent.CTI_ONCALLACTION, instance.id, function(e){
				if (instance.struct.version === 'DYN2') {
					instance.doCmdIncludeComponents('cti.callaction', {
						EVENT: e
					}, instance.runtime, instance.id, true);
				} else {
					instance.doCmdIncludeComponents("P-237");
				}
			});
			//CTI消息8 PARTYADDED
			project.events.register($CONST.CTIEvent.CTI_PARTYADDED, instance.id, function(){
				if (instance.struct.version === 'DYN2') {
					instance.doCmdIncludeComponents('cti.partyadded', {}, instance.runtime, instance.id, true);
				} else {
					instance.doCmdIncludeComponents("P-227");
				}
			});
			//CTI消息9 PARTYDELETED
			project.events.register($CONST.CTIEvent.CTI_PARTYDELETED, instance.id, function(){
				if (instance.struct.version === 'DYN2') {
					instance.doCmdIncludeComponents('cti.partydeleted', {}, instance.runtime, instance.id, true);
				} else {
					instance.doCmdIncludeComponents("P-228");
				}
			});
			//CTI消息22 PARTYCHANGED
			project.events.register($CONST.CTIEvent.CTI_PARTYCHANGED, instance.id, function(){
				if (instance.struct.version === 'DYN2') {
					instance.doCmdIncludeComponents('cti.partychanged', {}, instance.runtime, instance.id, true);
				} else {
					instance.doCmdIncludeComponents("P-229");
				}
			});

			//CTI消息74 DATAUPDATED
			project.events.register($CONST.CTIEvent.EVT_DATAUPDATED, instance.id, function(){
				if (instance.struct.version === 'DYN2') {
					instance.doCmdIncludeComponents('cti.dataupdated', {}, instance.runtime, instance.id, true);
				} else {
					instance.doCmdIncludeComponents("P-230");
				}
			});
			return instance;
		},

		/**
		 * 将实例存活，并放入实例集合
		 * @method live
		 * @param instance {object} 动态页面实例
		 * @param container {dom} 绘制动态页面实例的容器
		 * @param [objective] {object} objective数据实例
		 */
		live : function(instance, container, objective) {
			var runtime = instance.runtime, project = instance.project;
			container.attr('data-dyn', instance.struct.id);
			instance.init(objective);
			runtime.container = container;

			// 注册框架客户切换时的消息回调
			project.events.register($CONST.ModuleEvent.ECM_CUSTOMERCHANGE, instance.id, function(){
				// 如果实例客户指向的是框架客户
				if (instance.customer === project.customer) {
					// 收听所有已注册的用户表字段
					var cust = project.customer.clone();
					for (var field in instance.bindCustomerProperties) {
						instance.fireValue(field, runtime, {
							type: 0,
							data: cust.getValue(instance.bindCustomerProperties[field]),
							unbind: true
						})
					}
				}
			});
			// 页面加载
			instance.initCmd(runtime);
			// 一次性读取当前页面的所有结构（包含嵌套的组件结构）
			// this.collectStruct(instance.struct)
			this.startStep(runtime);
			// 页面绘制后
			instance.afterRender();
			return instance;
		},

		startStep : function(runtime) {
			if (runtime.page) {
				// 当前页面在加载命令中执行了子页面跳转指令
				return;
			}

			var struct = runtime.struct, firstId;
			if (struct.version == 'DYN2') {
				$.each(struct.flows, function(i, v) {
					if (v.source == 'BEGIN') {
						firstId = v.target;
						return false;
					}
				})
			} else
				firstId = runtime.struct.BN;
			if (! firstId)
				return $F.err('Dyn - startStep', '动态页面中没有发现可用子页面首页');

			var page = this.getPage(runtime, firstId);
			if (! page)
				return $F.err('Dyn - startStep', '无法加载子页面：' + firstId);

			runtime.flows.push({
				id: page.id,
				name: page.name
			});
			runtime.page = page;
			this.draw(runtime);
		},

		/**
		 * 进入实例的下一个节点页面
		 * @method nextStep
		 * @param runtime {object} 当前运行环境
		 */
		nextStep : function(runtime) {
			var a = this;
			var pageId = a.getNextPageId(runtime);
			if (! pageId) {
				$F.err('Dyn - nextStep', '不存在下一步：' + runtime.page.id);
				return false;
			}
			var page = a.getPage(runtime, pageId);
			if (! page)
				$F.err('Dyn - nextStep', '无法加载子页面：' + pageId);

			runtime.page = page;
			runtime.flows.push({
				id: pageId,
				name: runtime.page.name
			})
			if ($E.device == 'mobile') {
				if (runtime.instance.extendFixed) {
					runtime.instance.extendFixed.empty().html('').remove();
					delete runtime.instance.extendFixed;
				}
				if (runtime.instance.extendBSMenu) {
					runtime.instance.extendBSMenu.empty().html('').remove();
					delete runtime.instance.extendBSMenu;
				}
			}
			a.drawPage(runtime);

			if ($E.device == 'mobile') {
				runtime.project.addStep(runtime.instance);
				runtime.project.arrangementStack(runtime.instance);
			}
			return true;
		},

		/**
		 * 寻找动态页面中的下一个子节点GUID
		 * @method nextPage
		 * @param runtime {object} 当前运行环境
		 * @return {string} 动态页面流中的下一个页面GUID
		 */
		getNextPageId : function(runtime) {
			//console.log(runtime)
			var currentPageId = runtime.page.id, i = 0, o, v;
			if (runtime.struct.version == 'DYN2') {
				while (o = runtime.struct.flows[i++]) {
					if (o.source == currentPageId) {
						if (! o.exp) // 表达式为空时即无条件跳转
							return o.target;

						if (o.type) {
							// 判断条件为计算公式
							try {
								if (runtime.instance.dc.evalCmd(o.exp, {}, runtime))
									return o.target;
							} catch(e) {
								console.error(e);
							}
						} else {
							// 判断条件为属性值
							var x = runtime.instance.getValue(o.exp, runtime);
							if (x == undefined || x == null)
								throw new Error('instance getNextPageId error');

							if (parseInt(x))
								return o.target;
						}
					}
				}
			} else {
				while (o = runtime.struct.Flow[i++]) {
					//console.log(o);
					if (o.B == currentPageId) {
						if (o.E == 'END')
							return null;

						if (o.F == 1) {
							// 判断条件为计算公式
							if (o.CA == '')
								return o.E;
							try {
								if (runtime.instance.dc.doEval(o.CA, {}, runtime))
									return o.E;
							} catch(e) {
								console.error(e);
							}
						} else {
							// 判断条件为属性值
							if (o.PN == '')
								return o.E;
							var x = runtime.instance.getValue(o.PN, runtime);
							if (x == undefined || x == null)
								throw new Error('instance getNextPageId error');

							if (parseInt(x))
								return o.E;
						}
					}
				}
			}
			$F.err('Dyn - getNextPageId', '没有找到可用的下一页, 当前页ID：' + currentPageId);
			return null;
		},

		/**
		 * 进入实例的上一个节点页面
		 * @method prevStep
		 * @param runtime {object} 当前运行环境
		 */
		prevStep : function(runtime) {
			if (runtime.flows.length > 1) {
				runtime.flows.pop();
				var pageId = runtime.flows[runtime.flows.length-1].id;
				runtime.page = this.getPage(runtime, pageId);
				if ($E.device == 'mobile') {
					if (runtime.instance.extendFixed) {
						runtime.instance.extendFixed.empty().html('').remove();
						delete runtime.instance.extendFixed;
					}
					if (runtime.instance.extendBSMenu) {
						runtime.instance.extendBSMenu.empty().html('').remove();
						delete runtime.instance.extendBSMenu;
					}
				}
				this.drawPage(runtime);
				if ($E.device == 'mobile')
					runtime.project.arrangementStack(runtime.instance);
				return true;
			}
			return false;
		},

		/**
		 * 跳跃到实例路径的某个步骤
		 * @method jumpStep
		 * @param runtime {object} 当前运行环境
		 * @param pageId {string} 子页面ID
		 */
		jumpStep : function(runtime, pageId, force) {
			if (!runtime.page || pageId != runtime.page.id) {
				var a = this;
				if (force) {
					runtime.page = this.getPage(runtime, pageId);
					if (! runtime.page) {
						$F.err('Dyn - jumpStep', '跳转失败，没有找到子页面：' + pageId);
					} else {
						runtime.flows.push({
							id: runtime.page.id,
							name: runtime.page.name
						});
						if ($E.device == 'mobile') {
							if (runtime.instance.extendFixed) {
								runtime.instance.extendFixed.empty().html('').remove();
								delete runtime.instance.extendFixed;
							}
							if (runtime.instance.extendBSMenu) {
								runtime.instance.extendBSMenu.empty().html('').remove();
								delete runtime.instance.extendBSMenu;
							}
						}
						this.drawPage(runtime);
						if ($E.device == 'mobile') {
							runtime.project.addStep(runtime.instance);
							runtime.project.arrangementStack(runtime.instance);
						}
					}
				} else {
					$F.confirm($F.lang('dyn.jumpStep'), function() {
						var flows = [];
						for(var i = 0; i < runtime.flows.length; i++) {
							flows.push(runtime.flows[i]);
							if (runtime.flows[i].id == pageId) {
								runtime.flows = flows;
								runtime.page = a.getPage(runtime, pageId);
								a.drawPage(runtime);
								return;
							}
						}
						$F.err('Dyn - jumpStep', '跳转失败，没有找到子页面：' + pageId);
					});
				}
				return true;
			}
			return false;
		},

		/**
		 * 判断当前子页面是否具有下一页
		 * @method hasNextPage
		 * @param runtime {object} 动态页面运行环境
		 * @return {boolean}
		 */
		hasNextPage : function(runtime) {
			var i = 0, o;
			if (runtime.struct.version == 'DYN2') {
				while (o = runtime.struct.flows[i++]) {
					if (o.source == runtime.page.id)
						return true;
				}
			} else {
				while (o = runtime.struct.Flow[i++]) {
					if (o.B == runtime.page.id && !o.E.startWith('END'))
						return true;
				}
			}
			return false;
		},

		/**
		 * 获取某个子页面的数据
		 * @method getPage
		 * @param runtime {object} 动态页面运行环境
		 * @param subPageId {string} 子页面GUID
		 * @return {object} 子页面数据
		 */
		getPage : function(runtime, subPageId) {
			if (arguments.length < 3)
				return this._getPage(runtime.project, runtime.struct, subPageId);
			else {
				var project = arguments[0], struct = arguments[1], subPageId = arguments[2];
				return this._getPage(project, struct, subPageId);
			}
		},

		_getPage: function(project, struct, subPageId) {
			var page = struct.pages[subPageId];
			if (page == undefined) {
				page = this.loadPage(project, struct, subPageId);
				struct.pages[subPageId] = page;
			}
			return page;
		},

		/**
		 * 从服务器中加载动态页面流中的一个页面
		 * @method loadPage
		 * @param runtime {object} 动态页面运行环境
		 * @param subPageId {string} 子页面GUID
		 * @return {object} 子页面数据
		 */
		loadPage : function(project, struct, subPageId) {
			var start = new Date(), page, dbpool = project.getParam('CFGDBP');
			$F.logGroup('Dyn - loadPage', '加载子页面: ' + subPageId + '@' + struct.id);

			if (struct.version == 'DYN2') {
				var requestData;
				if ($E.publishVersion)
					requestData = $F.sqlbean2({VE:'publish.dyn.page.get'}, project.token, dbpool, [struct.guid, subPageId, struct.publishVersion])
				else
					requestData = $F.sqlbean2('VE.dyn.page.get', project.token, dbpool, [struct.guid, subPageId]);
				$F.dataService('SystemDo2', {
					data : requestData,
					digest: project.digest,
					async : false,
					success : function(data) {
						if (data.code > 0) {
							if (data.value[0].count > 0)
								page = JSON.parse(data.value[0].fields[0][0]);
						} else
							$F.alert(data.message);
					}
				});
			} else {
				$F.dataService('SysCacheJSON', {
					data : {
						Key : project.token,
						DBKey : '',
						Name : 'Dyn.Build',
						O : 'S',
						MainID : struct.id,
						SubID : subPageId,
						lang: ''
					},
					digest: project.digest,
					async : false,
					success : function(data) {
						if (data.code > 0) {
							page = data.value;
							struct.CtlCmdList = $.extend(struct.CtlCmdList, page.PCmd)
						} else
							$F.alert(data.message);
					}
				});
			}
			$F.logGroupEnd('Dyn - loadPage', '加载子页面完成，耗时' + (new Date() - start) + 'ms', page);
			return page;
		},

		/**
		 * @param runtime
		 * @param id {string} 引用的复用面板ID
		 * @param embId {string} 父面板ID
		 */
		reuseCtl : function(runtime, id, embId) {
			var ctl;
			$.each(runtime.page.ctls, function() {
				if (this.id == id) {
					ctl = this;
					return false;
				}
			})
			if (ctl) {
				ctl = $.extend(true, [], ctl);
				ctl.props.local.cloned = true;
				var xe = function(ctl) {
					ctl.id = embId + '-' + ctl.id;
					if (ctl.ctls) {
						$.each(ctl.ctls, function() {
							xe(this);
						})
					}
				}
				xe(ctl);
			}
			return ctl;
		},

		/**
		 * 批量载入组件
		 */
		loadComponents : function(runtime, ids, callback) {
			var a = this, project = runtime.project;
			if (! ids.length)
				return;

			if (runtime.struct.version == 'DYN2') {
				// for dyn2
				var dataRequest = {
					token: project.token,
					dbPool: project.getParam('CFGDBP'),
					sqls: []
				}
				if ($E.publishVersion) {
					$.each(ids, function(i, id) {
						dataRequest.sqls.push({
							areaKey: 'VE',
							key: 'publish.dyn.get',
							params: {
								C1: id,
								C2: runtime.struct.publishVersion
							}
						})
						dataRequest.sqls.push({
							areaKey: 'VE',
							key: 'publish.dyn.page.findAll',
							params: {
								C1: id,
								C2: runtime.struct.publishVersion
							}
						})
						dataRequest.sqls.push({
							areaKey: 'VE',
							key: 'publish.dyn.i18n.findAll',
							params: {
								C1: id,
								C2: runtime.struct.publishVersion
							}
						})
					})
				} else {
					$.each(ids, function(i, id) {
						dataRequest.sqls.push({
							key: 'dyn2.get',
							params: {
								C1: id
							}
						})
						dataRequest.sqls.push({
							key: 'VE.dyn.page.findAll',
							params: {
								C1: id
							}
						})
						dataRequest.sqls.push({
							key: 'VE.dyn.i18n.findAll',
							params: {
								C1: id
							}
						})
					})
				}
				$F.dataService('SystemDo2', {
					data: dataRequest,
					digest: project.digest,
					async : false,
					success: function(data) {
						if (data.code < 0)
							return $F.err('Dyn - loadComponents', data.message);

						// console.group('Dyn - loadComponents', '组件结构批量加载完成', data);
						// console.log(data.value);
						if (ids.length * 3 !== data.value.length)
							return $F.err('Dyn - loadComponents', '组件结构获取接口发生异常', data);
						$.each(ids, function(i, id) {
							var bean = $F.makeJsonBean(data.value[i*3]),
								i18n = {}, content, property, prop;
							if (! bean) {
								$F.err('组件结构为空或异常：' + id);
								return;
							}
							if (! bean.CONTENT) {
								console.error('空的组件页面内容', bean);
								content = {
									cmds: {
										internal: {},
										extend: {}
									},
									flows: [],
									pages: {},
									properties: {},
									styles: {}
								}
							} else
								content = JSON.parse(bean.CONTENT);
							var struct = {
								id: bean.DYNID2,
								guid: bean.DYNID,
								stateful: bean.STATEFUL,
								theme: bean.THEME,
								trackablity: bean.TRACKABLITY,
								cmds: content.cmds,
								flows: content.flows,
								pages: content.pages,
								styles: content.styles || {},
								props: {
									pub: {},
									local: {}
								},
								i18n: {},
								local: content.local || {},
								version: runtime.struct.version, // DYN还是DYN2
								publishVersion: $E.publishVersion ? bean.VERSION : '', // 发布的版本号，只有$E.publishVersion配置有值才会去获取
								publishedTime: bean.PUBLISHEDTIME
							};
							if (data.value[i*3 + 2].count > 0) {
								$.each(data.value[i*3+2].fields, function(i, v) {
									struct.i18n[v[0]] = JSON.parse(v[1]);
								})
							}
							for(var key in content.properties) {
								for(var name in content.properties[key]) {
									prop = content.properties[key][name];
									property = {
										ns: key,
										field: name,
										t: prop.t
									};
									if (prop.v !== undefined)
										property.v = prop.v;
									if (prop.e !== undefined)
										property.e = prop.e;
									if (key == 'var' || key == 'tabc') {
										struct.props.local[key + '.' + name] = property
									} else {
										struct.props.pub[key + '.' + name] = property
									}
								}
							}
							// 合并所有子页面在主结构中
							try {
								$.each(data.value[i*3 + 1].fields, function(i, row) {
									// console.log(row)
									var page = struct.pages[row[0]];
									if (page) {
										$.extend(page, JSON.parse(row[1]))
									} else
										$F.err('Ctls - loadComponents', '组件结构中未发现子页面: ' + row[0])
								})
							} catch(e) {
								$F.err('Ctls - loadComponents', e)
							}
							a.structs[project.id][runtime.struct.version][struct.guid] = struct;
							$F.log('Dyn - loadComponents', '获取组件结构：' + struct.id, struct);
							if ($.isFunction(callback))
								callback(id, struct);
						})
					}
				})
			} else {
				// for original dyn
				$F.dataService('JSGenerateServlet4', {
					data: {
						token : project.token,
						dbPool : '',
						name : "Dyn.ZJ.Structs",
						zjs: ids
					},
					digest: project.digest,
					async : false,
					success: function(data) {
						if (data.code < 0)
							return $F.err('Dyn - loadComponents', data.message);

						//console.group('Dyn - loadComponents', '组件结构批量加载完成', data);
						// console.log(data.value);
						for(var key in data.value) {
							//console.log(data.value[key]);
							var struct = data.value[key];
							struct.id = struct.GroupId;
							struct.guid = struct.GroupId;
							struct.name = struct.na;
							struct.LocList = struct.LocList || {};
							struct.CtlCmdList = {};
							struct.version = runtime.struct.version;
							delete struct.na;

							$.each(struct.pages, function(i, page) {
								$.extend(struct.CtlCmdList, page.PCmd);
							})
							a.structs[project.id][runtime.struct.version][struct.guid] = struct;
							if (! struct.id)
								$F.err('dyn - loadComponents', '组件结构载入发生异常！ID=' + key, struct);
							else {
								$F.log('Dyn - loadComponents', '获取组件结构：' + struct.id, struct);
								if ($.isFunction(callback))
									callback(key, struct);
							}
						}
						//console.groupEnd();
					}
				})
			}
		},

		/**
		 * 弹出一个动态页面窗口
		 * @method popup
		 * @param {string} id 窗口ID
		 * @param {string} dynId 动态页面GUID
		 * @param {string} oId objectiveGuid
		 * @param {string} otId 市场活动ID
		 * @param {instance} parentInstance 打开动态页面窗口的父实例
		 * @param {string} cmdId 执行关闭窗口前回调的命令组ID
		 * @param {object} parameters 启动环境
		 * @param {object} [options] 窗口的可选属性
		 *  {string} [options.title] 窗口标题
		 *  {integer} [options.width] 窗口宽度
		 *  {integer} [options.height] 窗口高度
		 *  {integer} [options.left] 窗口定位left坐标，-1为自动居中
		 *  {integer} [options.top] 窗口定位top坐标，-1为自动居中
		 *  {boolean} [options.minable] 窗口是否可最小化
		 *  {boolean} [options.maxable] 窗口是否可最大化
		 *  {boolean} [options.closable] 窗口是否可关闭
		 * @return instance 打开在窗口中的动态页面实例
		 */
		popup : function(id, dynId, oId, otId, parentInstance, cmdId, parameters, options) {
			options = $.extend({
				id: id || $F.randomString(8),
				title: '',
				width : 300,
				height : 200,
				model : true,
				minable: true,
				maxable: true,
				closable: true
			}, options);
			// console.info(options);

			if ($('#' + id + '_window').length > 0) {
				// 窗口已经被创建
				var dialog = H5Utils.window(options);
				return dialog.data('instance');
			}

			var dialog = H5Utils.window(options);
			var project = parentInstance.project;
			if (! dynId) {
				var objectiveType = project.getObjectiveType(otId);
				if (! objectiveType)
					return $F.alert('市场活动不存在：' + otId);
				dynId = objectiveType.addinCmtId;
			} else {
				if (dynId.length > 6) {
					$F.dataService('SystemDo2', {
						data: $F.sqlbean('Dyn.group.getByGuid', project.token, [dynId]),
						digest: project.digest,
						async: false,
						success: function(data) {
							if (data.code > 0) {
								if (data.value[0].count > 0)
									dynId = $F.makeJsonBean(data.value[0]).GROUP_IDA;
								else
									return $F.alert('无效的动态页GUID: ' + dynId);
							} else
								return $F.alert(data.message);
						}
					})
				}
			}
			var instance = this.createInstance(project, dynId, otId, oId, parameters, true);
			var closeBtn = $(".fancybox-close", dialog), title = $(".fancybox-title", dialog), container = $(".fancybox-content", dialog);
			title.text(options.title || title.text());
			//console.log(instance);
			this.live(instance, container);
			var cmdValues = instance.runtime.struct.CmdList['P1000'];
			if (cmdValues)
				instance.doCmd('P1000', cmdValues, instance.runtime);
			// console.log(instance);

			options.beforeClose = function(){
				//关闭页面时候先回调页面-240命令组
				parameters.CLOSABLE = 1,
					instance.doCmd('P-240', parameters, instance.runtime);
				if (cmdId.trim())
					parentInstance.doCmd('P' + cmdId, parameters, parentInstance.runtime);
				if (! parameters.CLOSABLE || parameters.CLOSABLE <= 0) {
					// console.log('页面回调控制不允许关闭');
					return false;
				}
				return true;
			}

			options.afterClose = function() {
				// 销毁实例
				instance.destroy();
			}

			dialog.data("instance", instance);
			instance.window = dialog;
			return instance;
		},

		/**
		 * 获得获得全局命令中的某个命令组
		 * for old version
		 *
		 *	命令组结构: {
		 *		key: {string} 命令组编号，格式为'Px',
		 *		value: {array} 命令队列
		 *	}
		 *
		 * @method getPCmd
		 * @param runtime {object} 实例对象
		 * @param cmdId {string} 全局命令的GUID
		 * @return {map} 全局命令组
		 */
		getPCmd : function(runtime, cmdId) {
			//console.log('调用全局命令组：' + cmdId);
			var a = this;
			if (!! a.pcmds[cmdId])
				return a.pcmds[cmdId];
			else {
				var result;
				$F.dataService('SysCacheJSON', {
					data : {
						Key : runtime.project.token,
						DBKey : '',
						Name : 'Dyn.Cmd',
						O : 'M',
						MainID : cmdId,
						SubID : '',
						lang: $E.language
					},
					digest: runtime.project.digest,
					async : false,
					success : function(data) {
						if (data.value) {
							result = data.value.CmdList['P' + cmdId];
							if (result) {
								a.pcmds[cmdId] = result;
								$F.log('Dyn - getPCmd', '获取全局命令组[' + cmdId + ']成功');
							}
						} else
							$F.err('Dyn - getPCmd', '获取全局命令组[' + cmdId + ']失败')
					}
				});
				return result;
			}
		},

		/**
		 * 动态页面创建UI渲染实例，创建动态组件及初始化数据
		 * @method draw
		 * @param runtime {object} 页面运行环境
		 */
		draw : function(runtime) {
			var a = this, start = new Date(), skin;
			if (runtime.component)
				$F.logGroup('Dyn - draw', '开始绘制组件UI: ' + runtime.component.id);
			else
				$F.logGroup('Dyn - draw', '开始绘制动态页面UI: ' + runtime.struct.id);

			if (runtime.struct.version === 'DYN2')
				skin = runtime.struct.theme;
			else
				skin = runtime.struct.Style;
			if (skin)
				Helper.loadCSS($E.url + '/css/skin/' + skin + '.css', 'SKIN_' + skin);

			// 赋值动态页面容器属性
			if (skin) {
				if (runtime.struct.version === 'DYN2')
					runtime.container.addClass(skin + ' dyn2');
				else
					runtime.container.addClass(skin);
			}

			if (runtime == runtime.instance.runtime) {
				// 当前环境为主页面
				if (skin) {
					// 同时给tab标签赋值风格
					runtime.container.data('tab') && runtime.container.data('tab').addClass(skin)
				}
				runtime.shell = $("<div class='dyn_shell' />");
				if ($E.device !== 'mobile') {
					// 绘制页面流转信息
					if (runtime.struct.FlowShow == 2 || runtime.struct.trackablity) {
						runtime.shell.addClass("dyn_shell_nav");
						let nav = $("<div class='dyn_nav' />").appendTo(runtime.container);
						runtime.navBar = $('<div style="position:relative;height:100%;width:auto" />').appendTo(nav);
					}

					// 绘制简单的工单流程
					if (runtime.instance.WO_SIMPLE_FLOWS) {
						runtime.shell.addClass("dyn_shell_nav");
						$F.nextTick(() => { // 避免有时候浮动层页面还没绘制到浏览器上就开始jsplumb，造成位置计算问题
							var nav = $("<div class='dyn_nav' />").appendTo(runtime.container);
							runtime.navBar = $('<div style="position:relative;height:100%;width:auto" />').appendTo(nav);
							a.drawWoNav(runtime);
						});
					}

					runtime.instance.leftSideBar = $('<div class="dyn_sidebar left" />').appendTo(runtime.container);
					runtime.instance.rightSideBar = $('<div class="dyn_sidebar right" />').appendTo(runtime.container);
					runtime.instance.topSideBar = $('<div class="dyn_sidebar top" />').appendTo(runtime.container);
					runtime.instance.bottomSideBar = $('<div class="dyn_sidebar bottom" />').appendTo(runtime.container);
				}

				a.drawPage(runtime);
				$('.page-loading', runtime.container).remove();
				if (runtime.struct.cp) {
					// 当前主页面为测试组件
					const testWrapper = $('<div class="cp_test" />')
					const testBox = $('<div class="dyn_test wrapper" style="width: 300px" />').appendTo(testWrapper);
					testWrapper.append(runtime.shell.css({
						position: 'absolute',
						left: '303px'
					}));
					testWrapper.wisSplitter({
						defA: 300,
						minA: 200,
						handleGrab: true,
						dockableB: true
					});
					require(['js/dynTest' + $E.jsv], function (test) {
						test.cpTest(runtime, testBox);
					})
					runtime.container.append(testWrapper);
				} else {
					runtime.container.append(runtime.shell);
				}
			} else if (runtime.struct.Que) {
				// 问卷组件
				// 自动打开上下页导航
				runtime.container.css('overflow', "hidden");
				// 绘制页面流转信息
				if (runtime.struct.FlowShow || runtime.struct.trackablity) {
					runtime.container.addClass("dyn_que_nav");
					let nav = $("<div class='dyn_nav' />").appendTo(runtime.container);
					runtime.navBar = $('<div style="position:relative;height:100%;width:auto" />').appendTo(nav);
				}
				// 页面绘图区
				runtime.shell = $('<div class="dyn_que_box" />');
				// 页面动作条
				if (runtime.instance.getValue('var.act_hide', runtime, 0) !== 1) {
					let nav = $('<div class="dyn_que_act" />').appendTo(runtime.container);
					if (runtime.instance.getValue('var.act_left', runtime, 0) == 1)
						nav.css('textAlign', 'left');
					if (runtime.flows.length > 1 && runtime.instance.getValue('var.act_hideprev', runtime, 0) !== 1) {
						$('<button type="button" class="default">' + $F.lang('nav.prev') + '</button>').appendTo(nav).click(function(){
							a.prevStep(runtime);
						})
					}
					if ($D.hasNextPage(runtime) && runtime.instance.getValue('var.act_hidenext', runtime, 0) !== 1) {
						$('<button type="button" class="default">' + $F.lang('nav.next') + '</button>').appendTo(nav).click(function(){
							a.nextStep(runtime);
						})
					}
					if (runtime.instance.getValue('var.act_hidesave', runtime, 0) !== 1) {
						$('<button type="button" class="default">' + $F.lang('nav.save') + '</button>').appendTo(nav).click(function(){
							runtime.component.save(true)
						})
					}
				} else
					box.css('bottom', 0);

				a.drawPage(runtime);
				$('.page-loading', runtime.container).remove();
				runtime.container.append(runtime.shell);
			} else {
				runtime.shell = runtime.container;
				a.drawPage(runtime);
				$('.page-loading', runtime.container).remove();
			}

			runtime.shell.viewer({
				filter(image) {
					return $(image).hasClass('jquery_viewer');
				}
			});
			if (runtime.component)
				$F.logGroupEnd('Dyn - draw', '完成组件UI绘制:' + runtime.component.id + '，耗时' + (new Date() - start) + 'ms');
			else
				$F.logGroupEnd('Dyn - draw', '完成动态页面UI绘制：' + runtime.struct.id + '，耗时' + (new Date() - start) + 'ms');
			return this;
		},

		drawPage: function(runtime) {
			const a = this;
			// 绘制页面流转信息
			if (runtime.struct.FlowShow || runtime.struct.trackablity) {
				a.drawNav(runtime);
				/*
				runtime.navBar.draggable({
					axis : "x",
					drag : function( event, ui ) {
						ui.position.left = Math.min( 0, ui.position.left );
						ui.position.left = Math.max( nav.width() - runtime.navBar.outerWidth(), ui.position.left);
					}
				});
				*/
			}

			runtime.instance.beforeDraw(runtime);
			// $('.dyn_box', runtime.shell).hide();
			var box = $('.dyn_box.page_' + runtime.page.id, runtime.shell);
			if (box.length) {
				// page box already exist, show it
				box.show();
			} else {
				box = $('<div class="dyn_box page_' + runtime.page.id + '" />').appendTo(runtime.shell);
				// 赋值动态页面容器属性
				if (runtime.struct.version == 'DYN2') {
					//console.log('赋值主页面风格：', runtime.struct.styles)
					box.css(runtime.struct.styles);
				} else {
					box.css('height', '100%');
				}

				// 判断节点页面是否有最大宽度
				var screenMaxWidth = runtime.project.getParam('SCMAXW');
				if (screenMaxWidth) {
					box.css({
						maxWidth: screenMaxWidth + 'px'
					})
				}
				a.drawCtls(runtime.page.ctls, box, runtime, runtime.namespace);
			}
			runtime.instance.afterDraw(runtime);
			// 绘制框架导航按钮
			if (runtime == runtime.instance.runtime && ! runtime.instance.inner) {
				if (runtime.struct.version == 'DYN2')
					Ctls2.action(runtime);
				else
					Ctls.action(runtime);
			}
		},

		/**
		 * 绘制导航区
		 */
		drawNav : function(runtime) {
			var jsp = jsPlumb.getInstance({
					HoverPaintStyle : {
						strokeStyle : "#1e8151",
						lineWidth : 2
					},
					ConnectionOverlays : [ [ "Arrow", {
						location : 1,
						id : "arrow",
						length : 8,
						width : 8,
						foldback : 0.8
					} ] ],
					Connector : [ "Flowchart", {
						curviness : 1
					} ],
					Anchors : [ "RightMiddle", "LeftMiddle" ],
					Container : runtime.navBar
				}),
				jsp_paintStyle =  {
					strokeStyle:"#7AB02C",
					fillStyle:"transparent",
					radius:2,
					lineWidth:2
				},
				jsp_endpointStyle = {
					fillStyle:"#1e8151",
					radius:2
				};

			runtime.navBar.empty();
			var p, $this = this, lo = 5;
			$.each(runtime.flows, function(i, node){
				var el = $('<a href="javascript:;" class="dyn_flow_el" />')
					.text(node.name)
					.css({
						left: lo + 'px'
					})
					.click(function(e) {
						e.preventDefault();
						$this.jumpStep(runtime, node.id);
					})
					.appendTo(runtime.navBar);
				lo = el.position().left + el.outerWidth();
				runtime.navBar.css('minWidth', lo);
				lo += 30;
				if (p !== undefined) {
					jsp.connect({
						source: p,
						target: el,
						paintStyle: jsp_paintStyle,
						endpointStyle: jsp_endpointStyle
					})
				}
				p = el;
			})
		},

		drawWoNav : function(runtime) {
			const jsp = jsPlumb.getInstance({
					HoverPaintStyle : {
						strokeStyle : "#1e8151",
						lineWidth : 2
					},
					ConnectionOverlays : [ [ "Arrow", {
						location : 1,
						id : "arrow",
						length : 8,
						width : 8,
						foldback : 0.8
					} ] ],
					Connector : [ "Flowchart", {
						curviness : 1
					} ],
					Anchors : [ "RightMiddle", "LeftMiddle" ],
					Container : runtime.navBar
				}),
				jsp_paintStyle = {
					strokeStyle : "#7AB02C",
					fillStyle : "transparent",
					radius : 2,
					lineWidth : 2
				}, jsp_paintStyleUnprocess = {
					strokeStyle : "#999999",
					fillStyle : "transparent",
					radius : 2,
					lineWidth : 2
				}, jsp_endpointStyle = {
					fillStyle : "#1e8151",
					radius : 2
				}, jsp_endpointStyleUnprocess = {
					fillStyle : "#999999",
					radius : 2
				};

			runtime.navBar.empty();
			let p, unprocessed = false, lo = 10;
			$.each(runtime.instance.WO_SIMPLE_FLOWS, function(i, node) {
				var el = $('<a href="javascript:;" class="dyn_flow_wo ' + (node.processed?'processed':'') + '" />')
					.text(node.NodeName)
					.data('entity', node)
					.css({
						left: lo + 'px'
					})
					.click(function(e) {
						e.preventDefault();
						runtime.instance.showWOFlowNode(node, el);
					})
					.appendTo(runtime.navBar);
				lo = el.position().left + el.outerWidth();
				runtime.navBar.css('minWidth', lo);
				lo += 30;
				if (p !== undefined) {
					jsp.connect({
						source: p,
						target: el,
						paintStyle: (node.processed ? jsp_paintStyle : jsp_paintStyleUnprocess),
						endpointStyle: (node.processed ? jsp_endpointStyle : jsp_endpointStyleUnprocess)
					})
				}
				p = el;

				Tipped.create(el, {
					ajax: {
						url: 'help/tipped',
						simulator: function(callback, element) {
							runtime.instance.workorder.getNodeInfo($(element).data('entity'), function(data) {
								if (data) {
									var html = '<div class="wonode_detail">'
										+ '<div><span class="handleByField">' + $F.lang('wo.handleBy') + ': </span><span class="handleByValue">' + data.handleBy + '</span></div>'
										+ '<div><span class="handleDateField">' + $F.lang('wo.handleTime') + ': </span><span class="handleDateValue">' + data.handleDate + '</span></div>'
										+ '</div>';
									callback($(html))
								} else {
									callback();
								}
							})
						}
					},
					cache: false,
					skin: 'light',
					radius: false,
					padding: false,
					size: 'large',
					position: 'bottom'
				});
			});


			var info = $('<div class="info" />');
			runtime.navBar.parent().parent().append(info);
			runtime.navBar.data('info', info);
		},

		/**
		 * 绘制UI控件
		 * @method drawCtls
		 * @param ctls {array} 需要绘制的控件队列
		 * @param box {dom} 绘制控件的容器
		 * @param runtime {object} 当前环境
		 * @param data {any} 当前的数据行（仅当绘制列表项数据时有效）
		 * @param rowNum {integer} 当前数据行的行号
		 */
		drawCtls : function(ctls, box, runtime, path, data, rowNum) {
			var a = this, c, caches = [], comWaitLoad = {};
			$.each(ctls, function(i, v) {
				if (v.unavailable) {
					// control is not available, disabled by some filter
					return;
				}

				try {
					c = drawCtl(v, box, runtime, path, data, rowNum);
					var widgetId = c.eid || c.id;
//					if (runtime.ctls[widgetId])
//						throw new Error('控件id发生冲突：' + widgetId);
					runtime.instance.attributes['curCtl'] = runtime.ctls[widgetId] = c;
					caches.push(c);

					if (c.type == 'COMPONENT' && c.componentId !== undefined) {
						// 自定义动态页面组件
						var comStruct = a.structs[runtime.project.id][runtime.struct.version][c.componentId]
						if (! comStruct) {
							if (! comWaitLoad[c.componentId])
								comWaitLoad[c.componentId] = [];
							comWaitLoad[c.componentId].push(c);
						} else {
							var com = Component.create(c.componentId, c, comStruct);
							if (c.initCmd)
								c.initCmd(com);
						}
					}
					var lastCtl = runtime.instance.attributes['curCtl'];
					if (lastCtl) {
						c.prevCtl = lastCtl;
						lastCtl.nextCtl = c;
					}
				} catch(e) {
					console.error(e);
				}
			});
			box.data('controls', caches);
			runtime.instance.sqlexec(runtime);

			// 加载自定义组件
			a.loadComponents(runtime, Object.keys(comWaitLoad), function(key, struct) {
				$.each(comWaitLoad[key], function(i, c) {
					var com = Component.create(key, c, struct);
					if (c.initCmd)
						c.initCmd(com);
				})
			})
		},


		/**
		 * 绘制UI控件
		 * @method drawCtlsBatch
		 * @param ctls {array} 需要绘制的控件队列
		 * @param runtime {object} 当前环境
		 * @param path {string} 当前路径
		 * @param data {any} 当前的数据行（仅当绘制列表项数据时有效）
		 * @param rowNum {integer} 当前数据行的行号
		 */
		drawCtlsBatch : function(ctls, runtime, path, data, rowNum) {
			var a = this, c, caches = [], comWaitLoad = {};
			$.each(ctls, function(i, v) {
				if (v.unavailable) {
					// control is not available, disabled by some filter
					return;
				}

				try {
					c = drawCtl(v, v.container, runtime, path, data, rowNum);
					v.ctl = c;
					runtime.instance.attributes['curCtl'] = runtime.ctls[c.id] = c;
					caches.push(c);

					if (c.type == 'COMPONENT' && c.componentId !== undefined) {
						// 自定义动态页面组件
						var comStruct = a.structs[runtime.project.id][runtime.struct.version][c.componentId]
						if (! comStruct) {
							if (! comWaitLoad[c.componentId])
								comWaitLoad[c.componentId] = [];
							comWaitLoad[c.componentId].push(c);
						} else {
							var com = Component.create(c.componentId, c, comStruct);
							if (c.initCmd)
								c.initCmd(com);
						}
					}
					var lastCtl = runtime.instance.attributes['curCtl'];
					if (lastCtl) {
						c.prevCtl = lastCtl;
						lastCtl.nextCtl = c;
					}
				} catch(e) {
					console.error(e);
				}
			});
			runtime.instance.sqlexec(runtime);

			// 加载自定义组件
			a.loadComponents(runtime, Object.keys(comWaitLoad), function(key, struct) {
				$.each(comWaitLoad[key], function(i, c) {
					var com = Component.create(key, c, struct);
					if (c.initCmd)
						c.initCmd(com);
				})
			})

			return caches;
		}
	};

	function drawCtl(v, box, runtime, path, data, rowNum) {
		if (runtime.struct.version == 'DYN2') {
			// 第二代动态页面
			return Ctls2[v.name](box, v, runtime, path, data, rowNum);
		} else {
			switch (v.cType) {
				case 1:
					if (v.o2 && v.o2.marquee == 2)
						return Ctls.timeline(box, v, runtime, data, rowNum);
					else
						return Ctls.label(box, v, runtime, data, rowNum);
				case 2:
					return Ctls.button(box, v, runtime, data, rowNum);
				case 3:
					return Ctls.checkbox(box, v, runtime, data, rowNum);
				case 4:
					return Ctls.combox(box, v, runtime, data, rowNum);
				case 6:
					return Ctls.datetime(box, v, runtime, data, rowNum);
				case 7:
					if (v.o2 && v.o2.format == 'box')
						return Ctls.pin(box, v, runtime, data, rowNum);
					else
						return Ctls.number(box, v, runtime, data, rowNum);
				case 8:
					return Ctls.text(box, v, runtime, data, rowNum);
				case 10:
					return Ctls.list(box, v, runtime, data, rowNum);
				case 11:
					return Ctls.listci(box, v, runtime, data, rowNum);
				case 13:
					return Ctls.radio(box, v, runtime, data, rowNum);
				case 14:
					return Ctls.panel(box, v, runtime, data, rowNum);
				case 15:
					return Ctls.table(box, v, runtime, data, rowNum);
				case 16:
					return Ctls.script(box, v, runtime, data, rowNum);
				case 17:
					return Ctls.progress(box, v, runtime, data, rowNum);
				case 18:
					return Ctls.tree(box, v, runtime, data, rowNum);
				case 19:
					return Ctls.page(box, v, runtime, data, rowNum);
				case 20:
					return Ctls.table2(box, v, runtime, data, rowNum);
				case 21:
					return Ctls.image(box, v, runtime, data, rowNum);
				case 22:
					return Ctls.audio(box, v, runtime, data, rowNum);
				case 23:
					return Ctls.iframe(box, v, runtime, data, rowNum);
				case 24:
					return Ctls.component(box, v, runtime);
				case 26:
					return Ctls.calendar(box, v, runtime, data, rowNum);
				case 27:
					if (v.o2 && v.o2.lol)
						return Ctls.lol(box, v, runtime);
					else
						return Ctls.link(box, v, runtime, data, rowNum);
				case 28:
					return Ctls.chart2(box, v, runtime, data, rowNum);
				case 29:
					return Ctls.video(box, v, runtime, data, rowNum);
				case 30:
					return Ctls.bsmenu(box, v, runtime);
				case 31:
					return Ctls.layoutBox(box, v, runtime);
				case 32:
					return Ctls.tab(box, v, runtime, data, rowNum);
				case 34:
					return Ctls.chart(box, v, runtime, data, rowNum);
				case 214:
					return Ctls.xitem(box, v, runtime, data, rowNum);
				case 215:
					return Ctls.splitter(box, v, runtime, data, rowNum);
				default:
					throw new Error('不能识别的控件:' + v.cType);
			}
		}
	}

	window.$D = $D;
	return $D;
});
